package com.artup.util.pdf;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;

import org.apache.commons.lang3.StringUtils;
import org.icepdf.core.pobjects.Page;
import org.icepdf.core.util.GraphicsRenderingHints;

import com.artup.pojo.ImageEntity;
import com.artup.util.file.FileUtil;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.CMYKColor;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfWriter;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class PdfUtil {

	/**
	 * 设置文字水印
	 * 
	 * @param srcPdfFile
	 * @param destPdfFile
	 * @param param
	 * @throws RuntimeException
	 */
	public static void addTextWaterMark(String srcPdfFile, String destPdfFile, PdfParam param)throws RuntimeException {
		PdfStamper stamper = null;
		try {
			if (StringUtils.isBlank(srcPdfFile) && param == null) {
				throw new RuntimeException("参数错误: 未指定输入PDF文件路径(srcPdfFilePath为空或param为空)");
			}
			srcPdfFile = StringUtils.isBlank(srcPdfFile) ? param.getSrcPdfFilePath() : srcPdfFile;

			if (StringUtils.isBlank(srcPdfFile)) {
				throw new RuntimeException("参数错误: 输入PDF文件路径为空(srcPdfFilePath为空)");
			}

			if (StringUtils.isBlank(destPdfFile) && param == null) {
				throw new RuntimeException("参数错误: 未指定输出PDF文件路径(destPdfFilePath为空或param为空)");
			}
			destPdfFile = StringUtils.isBlank(destPdfFile) ? param.getDestPdfFilePath() : destPdfFile;

			if (StringUtils.isBlank(destPdfFile)) {
				throw new RuntimeException("参数错误: 未指定输出PDF文件路径(destPdfFilePath为空)");
			}
			if(param == null){
				throw new RuntimeException("参数错误: 未设置参数(param为空)");
			}
			param.setSrcPdfFilePath(srcPdfFile);
			param.setDestPdfFilePath(destPdfFile);
			
			PdfReader reader = new PdfReader(srcPdfFile);
			stamper = new PdfStamper(reader, new FileOutputStream(destPdfFile));
			// 设置密码
			// stamper.setEncryption(userPassWord.getBytes(),
			// ownerPassWord.getBytes(), permission, false);
			
			BaseFont bf = param.getFont();
			bf = bf == null ? BaseFont.createFont(PdfParam.DEFAULT_FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED) : bf;
			
			int startPage = param.getStartPage();
			int endPage = param.getEndPage();
			int total = reader.getNumberOfPages();
			endPage = endPage > total || endPage < 1 ? total : endPage;
			String text = param == null || StringUtils.isBlank(param.getWaterMarkText()) ? "" : param.getWaterMarkText();
			
			PdfContentByte over = null;
			// 循环对每页插入水印
			for (int i = startPage; i <= endPage; i++) {
				over = stamper.getOverContent(i);
				
				// 增加文本
				over.beginText();
				over.setColorFill(CMYKColor.GRAY);
				over.setFontAndSize(bf, param == null ? PdfParam.DEFAULT_FONT_SIZE : param.getFontSize());
				over.setTextMatrix(param == null ? 0F : param.getMatrixX(), param == null ? 0F : param.getMatrixY());
				over.showText(text);
				
				//额外属性设置
				if(param.getTextBuilder() != null){
					try{
						param.getTextBuilder().textBuid(param, i);
					}catch(Exception e){
						log.error(e.getMessage());
					}
				}
				
				over.endText();
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new RuntimeException(e.getMessage());
		} finally {
			if (stamper != null) {
				try {
					stamper.close();
				} catch (DocumentException e) {
					log.error(e.getMessage(), e);
				} catch (IOException e) {
					log.error(e.getMessage(), e);
				}
			}
		}
	}

	/**
	 * 添加水印图片
	 * 
	 * @param srcPdfFile
	 * @param destPdfFile
	 * @param param
	 * @throws RuntimeException
	 */
	public static void addImageWaterMark(String srcPdfFile, String destPdfFile, PdfParam param)throws RuntimeException {
		PdfStamper stamper = null;
		try {
			if (StringUtils.isBlank(srcPdfFile) && param == null) {
				throw new RuntimeException("参数错误: 未指定输入PDF文件路径(srcPdfFilePath为空或param为空)");
			}
			srcPdfFile = StringUtils.isBlank(srcPdfFile) ? param.getSrcPdfFilePath() : srcPdfFile;

			if (StringUtils.isBlank(srcPdfFile)) {
				throw new RuntimeException("参数错误: 输入PDF文件路径为空(srcPdfFilePath为空)");
			}

			if (StringUtils.isBlank(destPdfFile) && param == null) {
				throw new RuntimeException("参数错误: 未指定输出PDF文件路径(destPdfFilePath为空或param为空)");
			}
			destPdfFile = StringUtils.isBlank(destPdfFile) ? param.getDestPdfFilePath() : destPdfFile;

			if (StringUtils.isBlank(destPdfFile)) {
				throw new RuntimeException("参数错误: 未指定输出PDF文件路径(destPdfFilePath为空)");
			}
			if(param == null){
				throw new RuntimeException("参数错误: 未设置参数(param为空)");
			}
			PdfReader reader = new PdfReader(srcPdfFile);
			stamper = new PdfStamper(reader, new FileOutputStream(destPdfFile));
			
			if(StringUtils.isBlank(param.getWaterMarkImagePath())){
				throw new RuntimeException("参数错误: 未设置参数(waterMarkImagePath为空)");
			}
			param.setSrcPdfFilePath(srcPdfFile);
			param.setDestPdfFilePath(destPdfFile);
			
			Image img = Image.getInstance(param.getWaterMarkImagePath());// 插入水印
			
			//水印图片位置
			img.setAbsolutePosition(param.getWaterMarkImageAbsoluteX(), param.getWaterMarkImageAbsoluteY());
			
			int startPage = param.getStartPage();
			int endPage = param.getEndPage();
			int total = reader.getNumberOfPages();
			endPage = endPage > total || endPage < 1 ? total : endPage;
			for (int i = startPage; i <= endPage; i++) {
				PdfContentByte under = stamper.getUnderContent(i);
				
				//额外属性设置
				if(param.getImageBuilder() != null){
					try{
						param.getImageBuilder().imageBuid(param, img, i);
					}catch(Exception e){
						log.error(e.getMessage());
					}
				}
				
				under.addImage(img);
			}
		}catch(Exception e){
			log.error(e.getMessage(), e);
			throw new RuntimeException(e.getMessage());
		}finally{
			if (stamper != null) {
				try {
					stamper.close();
				} catch (DocumentException e) {
					log.error(e.getMessage(), e);
				} catch (IOException e) {
					log.error(e.getMessage(), e);
				}
			}
		}
	}
	
	/**
	 * 将pdf文件指定页转成image
	 * 
	 * @param pdfFilePath
	 * @param imagePath
	 * @param page
	 * @return
	 */
	public static boolean convetToImage(String pdfFilePath, String imagePath, int page) {
		//文件后缀
		String ext = FileUtil.getExtension(imagePath);
		org.icepdf.core.pobjects.Document document = null;
		ImageWriter writer = null;
		BufferedImage image = null;
		try {
			float rotation = 0f;
			// 缩略图显示倍数，1表示不缩放，0.5表示缩小到50%
			float zoom = 1.0f;
			document = new org.icepdf.core.pobjects.Document();
			document.setFile(pdfFilePath);
			int pages = document.getNumberOfPages();
			page = page > pages ? 0 : page;
			image = (BufferedImage) document.getPageImage(page,	GraphicsRenderingHints.SCREEN, Page.BOUNDARY_CROPBOX, rotation, zoom);
			ImageIO.write(image, ext, new File(imagePath));
			return true;
		}catch(Exception e){
			log.error(e.getMessage(), e);
		}finally {
			if (image != null){
				image.flush();
			}
			if (writer != null){
				writer.dispose();
			}
			if(document != null){
				document.dispose();
			}
		}
		return false;
	}
	
	/**
	 * 生成pdf文件
	 * 
	 * @param destPdfFile
	 * @param param
	 * @throws RuntimeException
	 */
	public static void buildPdf(String destPdfFile, PdfParam param)throws RuntimeException{
		Document document = null;	// 文档
		try{
			if(param == null){
				throw new RuntimeException("参数错误: 未设置参数(param为空)");
			}
			
			destPdfFile = StringUtils.isBlank(destPdfFile) ? param.getDestPdfFilePath() : destPdfFile;
			if (StringUtils.isBlank(destPdfFile)) {
				throw new RuntimeException("参数错误: 未指定输出PDF文件路径(destPdfFilePath为空)");
			}
			
			param.setDestPdfFilePath(destPdfFile);
			
			/**
			 * PDF 页面框，默认采用 A4 纸张大小
			 */
			Rectangle pageRectangle = param.getRectangle() == null ? new Rectangle(PageSize.A4) : param.getRectangle();		// PDF 页面框
			document = new Document(pageRectangle);		// 创建一个文档
			
			// 建立一个书写器(Writer)与document对象关联，通过书写器(Writer)可以将文档写入到磁盘中
			PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(destPdfFile));

			// PDF版本
			writer.setPdfVersion(PdfWriter.PDF_VERSION_1_7);
			
			//页空白
			document.setMargins(param.getMarginLeft(), param.getMarginRight(), param.getMarginTop(), param.getMarginBottom());
			
			if(param.getDocumentBuilder() != null){
				param.getDocumentBuilder().beforeOpen(document, param);
			}
			
			document.open();	// 打开文档
			
			if(param.getDocumentBuilder() != null){
				param.getDocumentBuilder().postOpen(writer, document, param);
			}
			
		}catch(Exception e){
			log.error("设置 PDF 文档内容失败！", e);
			throw new RuntimeException(e.getMessage());
		}finally{
			if (document != null){
				try{
					document.close();
				}catch(Exception e){
					log.error("关闭 PDF 文档失败！", e);
				}
			}
		}
	}
	
	/**
	 * 把毫米值转成pt值
	 * 
	 * 1点(pt)=1/72(英寸)inch 
	 * 1英寸=25.4毫米mm
	 * 
	 * @param value
	 * @return
	 */
	public static float convertMm2Pt(float value){
		return convertInch2Pt(convertMm2Inch(value));
	}
	
	/**
	 * 将磅转成毫米
	 * @param value
	 * @return
	 */
	public static float convertPt2Mm(float value){
		return convertInch2Mm(convertPt2Inch(value));
	}
	
	/**
	 * 将毫米转成英尺
	 * 
	 * @param value
	 * @return
	 */
	public static float convertMm2Inch(float value){
		return (float)(value / 25.4);
	}
	
	/**
	 * 将英尺转磅
	 * @param value
	 * @return
	 */
	public static float convertInch2Pt(float value) {
         return value * 72f;
    }
	
	/**
	 * 150 DPI 时，像素转毫米
	 * @param pixel 像素
	 * @return 毫米
	 */
	public static float px2mm_150dpi(int pixel) {
		if(0 >= pixel) {
			return 0;
		} else {
			return (float) pixel / ImageEntity.MM2PX_150;
		}
	}

	/**
	 * 150 PPI 时，毫米转像素
	 * @param millimeter 毫米
	 * @return 像素
	 */
	public static int millimeter2pixel_150ppi(int millimeter) {
				
 		if(0 >= millimeter) {
			return 0;
		} else {
			return (int) (0.0393700787401575 * millimeter * 150);
		}
		
//		return (int) (0.0393700787401575 * millimeter * 150);
	}

	/**
	 * 150 PPI 时，厘米转像素
	 * @param centimeter 厘米
	 * @return 像素
	 */
	public static int centimeter2pixel_150ppi(int centimeter) {
		if(0 >= centimeter) {
			return 0;
		} else {
			return (int) (0.393700787401575 * centimeter * 150);
		}
	}
	
	/**
	 * 150 PPI 时，像素转点
	 * @param pixel 像素
	 * @return 点
	 */
	public static float pixel2point_150ppi(int pixel) {
		if(0 >= pixel) {
			return 0;
		} else {
			return (float) pixel / 150 * 72;
		}
	}

	/**
	 * 150 PPI 时，点转像素
	 * @param point 点
	 * @return 像素
	 */
	public static float point2pixel_150ppi(int point) {
		if(0 >= point) {
			return 0;
		} else {
			return (float) point / 72 * 150;
		}
	}
	
	/**
	 * 将英尺转成毫米
	 * 
	 * @param value
	 * @return
	 */
	public static float convertInch2Mm(float value) {
        return value * 25.4f;
    }
	
	/**
	 * 将磅转成英尺
	 * @param value
	 * @return
	 */
	public static float convertPt2Inch(float value) {
        return value / 72f;
    }
	
	@Setter
	@Getter
	public static class PdfParam {
		/**
		 * 默认字体
		 */
		public static final String DEFAULT_FONT = "fonts/msyh.ttf";
		
		/**
		 * 默认字体大小
		 */
		public static final float DEFAULT_FONT_SIZE = 16F;
		
		/**
		 * 原始文件路径
		 */
		private String srcPdfFilePath;

		/**
		 * 目标文件路径
		 */
		private String destPdfFilePath;

		/**
		 * 水印文字
		 */
		private String waterMarkText;
		
		/**
		 * 默认字体
		 */
		private BaseFont font;
		
		/**
		 * 字体大小
		 */
		private float fontSize = DEFAULT_FONT_SIZE;
		
		/**
		 * 开始页码
		 */
		private int startPage = 1;
		
		/**
		 * 结束页码
		 */
		private int endPage = -1;
		
		/**
		 * 当添加文字水印时，设置坐标x值
		 */
		private float matrixX;
		
		/**
		 * 当添加文字水印时，设置坐标y值
		 */
		private float matrixY;
		
		/**
		 * 水印图片路径
		 */
		private String waterMarkImagePath;
		
		/**
		 * 当添加水印图片时，水印图片位置x坐标值
		 */
		private float waterMarkImageAbsoluteX;
		
		/**
		 * 当添加水印图片时，水印图片位置y坐标值
		 */
		private float waterMarkImageAbsoluteY;
		
		/**
		 * 在添加水印图片时，需要额外设置属性时回调类
		 */
		private PdfImageWarterMarkBuilder imageBuilder;
		
		/**
		 * 在添加水印文字时，需要额外设置属性的回调类
		 */
		private PdfTextWarterMarkBuilder textBuilder;
		
		/**
		 * pdf文件页面大小
		 */
		private Rectangle rectangle;	// PDF 单页的大小
		
		/**
		 * 左边距
		 */
		private float marginLeft ;
		
		/**
		 * 右边距
		 */
		private float marginRight;
		
		/**
		 * 上边距
		 */
		private float marginTop; 
		
		/**
		 * 下边距
		 */
		private float marginBottom;
		
		/**
		 * 生成pdf文件时回调类
		 */
		private PdfDocumentBuilder documentBuilder;
		
		/**
		 * pdf内容,处理多页时设置pdf每页的内容
		 */
		private List<? extends Object> content;
		
		/**
		 * 额外参数
		 */
		private Map<String, Object> parameter = new java.util.concurrent.ConcurrentHashMap<String, Object>();
	}
	
	public static interface PdfImageWarterMarkBuilder {
		/**
		 * 
		 * @param param
		 * @param img
		 * @param currentPage
		 * @throws Exception
		 */
		public void imageBuid(PdfParam param, Image img, int currentPage) throws Exception;
	}
	
	public static interface PdfTextWarterMarkBuilder {
		/**
		 * 
		 * @param param
		 * @param currentPage
		 * @throws Exception
		 */
		public void textBuid(PdfParam param, int currentPage) throws Exception;
	}
	
	/**
	 * 生成pdf的回调
	 * 
	 * @author zzg
	 *
	 */
	public static interface PdfDocumentBuilder { 
		
		/**
		 * 在调用document.open()之前调用，因为当调用open后，document将不能设置Header- or Meta-information
		 * 
		 * @param document
		 * @param param
		 * @throws Exception
		 */
		public void beforeOpen(Document document, PdfParam param) throws Exception;
		
		/**
		 * 在调用document.open()之后调用，因为当调用open后，document将不能设置Header- or Meta-information
		 * 
		 * @param writer
		 * @param document
		 * @param param
		 * @throws Exception
		 */
		public void postOpen(PdfWriter writer, Document document, PdfParam param) throws Exception;
	}

	public static void main(String[] args) {
		System.out.println(PdfUtil.millimeter2pixel_150ppi(700));
	}
}
